<template>
  <div>
    <div class="container" style="padding: 15px">
      <div class="handle-box" style="margin-left: 8px; margin-bottom: 20px">
        <el-select
          v-model="queryParam.fkCourseName"
          filterable
          placeholder="请输入课程名称"
          style="width: 170px; margin-left: 10px"
        >
          <el-option label="所有课程" value=""></el-option>
          <el-option
            v-for="item in courses"
            :key="'query' + item"
            :label="item"
            :value="item"
          >
          </el-option>
        </el-select>
        <el-input
          clearable
          v-model="queryParam.examName"
          placeholder="考试名称"
          class="handle-input mr10"
          style="width: 200px; margin-left: 10px; margin-right: 10px"
        ></el-input>
        <el-button type="primary" icon="el-icon-search" @click="handleSearch"
          >搜索</el-button
        >
      </div>
      <el-card class="card" style="width: 384px" v-if="exams.length === 0">
        <div style="padding: 14px">
          <i class="el-icon-info" style="font-size: 30px"
            ><span
              style="
                font-size: 15px;
                height: 30px;
                line-height: 30px;
                vertical-align: middle;
              "
              >&nbsp;暂无数据！请在上方确认查找条件！</span
            ></i
          >
        </div>
      </el-card>
      <el-card
        v-for="(exam, index1) in exams"
        :key="index1"
        :body-style="{ padding: '0px' }"
        class="card"
      >
        <div style="padding: 14px">
          <div style="margin-bottom: 2px">
            <span class="span_label">考试名：</span>
            <el-tooltip
              class="item"
              effect="dark"
              :content="exam.examName"
              placement="top"
            >
              <span class="span_info" :title="exam.examName">{{
                exam.examName
              }}</span></el-tooltip
            >
          </div>
          <div style="margin-bottom: 4px">
            <span class="span_label">开考时间：</span>
            <span class="span_info" :title="exam.beginTime">{{
              formatDate(exam.beginTime)
            }}</span>
          </div>
          <div style="margin-bottom: 4px">
            <span class="span_label">结束时间：</span>
            <span class="span_info" :title="exam.endTime">{{
              formatDate(exam.endTime)
            }}</span>
          </div>
          <div style="margin-top: 10px 0 50px 0; float: right; padding-bottom: 10px">
            <el-button
              type="warning"
              round
              size="small"
              style="font-size: 14px; padding: 5px"
              icon="el-icon-edit"
              @click="examHandler(exam)"
              >进入考试</el-button
            >
          </div>
        </div>
      </el-card>
    </div>

    <el-dialog
    v-loading="dialogLoading"
      ref="toTestDialog"
      @close="testDialogClose"
      :fullscreen="isFull"
      :show-close="!isFull"
      :visible.sync="toTestVisible"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      width="600px"
    >
      <div v-if="step === 'check_face'">
        <div style="margin-left: 15px">
          <check-face
            v-if="step === 'check_face'"
            ref="faceCheck"
            @nextStep="nextStep"
          ></check-face>
          <div v-if="isUploaded" style="margin-top: 15px">
            <el-tag size="medium"
              >离考试开始还有：<timer
                v-if="step === 'check_face'"
                ref="timer"
                :remainTime="remainTime"
                @endTimer="firstEndTimer"
              ></timer
            ></el-tag>
            <el-button
              type="primary"
              style="margin-left: 20px"
              @click="openExam"
              :disabled="!isBegin || !isScreen"
              >进入考试</el-button
            >
          </div>
        </div>
      </div>
      <div slot="title" v-if="isFull" class="titlewin">
        <div style="float: left; margin-left: 50px">{{ choseExam.examName }}</div>
        <div style="float: right">
          <!-- <el-tag type="info" size="medium" style="margin-right:5px;">离考试结束还有：</el-tag> -->
          <el-tag type="primary" size="large" style="margin-right: 10px"
            >离考试结束还有：<timer
              ref="secondTimer"
              @endTimer="secondEndTimer"
              :remainTime="remainTime"
            ></timer
          ></el-tag>
          <el-button type="primary" @click="submitPaper" style="margin-right: 20px"
            >提交试卷</el-button
          >
        </div>
      </div>
      <div v-if="step === 'init_exam'">
        <el-container>
          <el-aside width="300px" style="margin-top: 25px">
            <div class="leftwin">
              <video id="video2" width="260" height="195" autoplay></video>
            </div>
          </el-aside>
          <el-main
            style="border-left-style: solid; border-width: 1px; overflow-y: auto"
            :style="'height:' + mainheight + 'px'"
            ><show-paper ref="rightpaper" :allQuestions="allQuestions"></show-paper
          ></el-main>
        </el-container>
        <!-- <div class="leftwin">1111</div>
        <div class="rightwin">
          11111
          <show-paper ref="rightpaper" :allQuestions="allQuestions"></show-paper>
        </div> -->
      </div>
    </el-dialog>
  </div>
</template>

<script>
import DateUtils from "../../utils/DateUtils.js";
import CheckFace from "./CheckFace.vue";
import Timer from "../common/Timer.vue";
import ShowPaper from "./ShowPaper.vue";
require("tracking/build/tracking-min.js");
require("tracking/build/data/face-min.js");
require("tracking/build/data/mouth-min.js");
require("tracking/examples/assets/stats.min.js");

var _this;

export default {
  components: { CheckFace, Timer, ShowPaper },
  data() {
    return {
      courses: [], //课程下拉菜单
      exams: [], //返回的考试列表
      choseExam: {}, //所点击的考试
      remainTime: 0, //倒计时总时间 ms
      systemTime: 0, //服务器系统时间
      queryParam: {
        //查询条件
        fkCourseName: "",
        fkExamName: "",
        statusList: [0, 1],
      },
      dialogLoading: false,
      isFull: false, //是否全屏显示
      isUploaded: false, //验证上传
      isBegin: false, //考试是否开始 -> 进入考试按钮是否可点击条件之一
      allQuestions: [], //试卷题目列表
      toTestVisible: false, //dialog显示
      step: "", //第几步 步名 check_face  init_paper
      hasFace: false, //是否检测到人脸
      mainheight: 0, //试卷显示区域 计算后的高度值
      videoStream: {}, //用户前置摄像头 返回流
      screenStream: {}, //屏幕共享返回流
      isScreen: false, //屏幕共享是否成功 -> 进入考试按钮是否可点击条件之一
      isSubmit: false, //是否已提交试卷
      isWebscoket: false, //websocket连接是否建立

      // 以下为websocet所需变量
      websocket: {}, // webscoket连接
      teacherMap: new Map(), // 教师map key-> userId value -> Object (pc...)
      studentId: sessionStorage.getItem("userId"),
      loginToken: sessionStorage.getItem("ClientToken"),
      websocketUrl: "ws://localhost:8989/wsserver/",
      isCaller: true,
      iceServer: {
        iceServers: [
          // {
           
          // },
          // {
            
          // },
        ],
      },
    };
  },
  computed: {},
  methods: {
    // 进入考试
    examHandler(exam) {
      let isChrome = navigator.userAgent.indexOf("Chrome") > -1; // 是否是谷歌
      console.log("谷歌" + isChrome); // 返回 true  则是 ，false  则不是
      if (isChrome === "true") {
        this.$confirm(
          "检测到当前浏览器不支持本系统的屏幕共享功能，请使用Chrome、Edge或其他谷歌内核浏览器再次进入！",
          "提示",
          {
            showCancelButton: false,
            confirmButtonText: "确认",
            type: "error",
          }
        ).catch(() => {});
      } else {
        this.choseExam = JSON.parse(JSON.stringify(exam));
        this.step = "check_face";
        this.toTestVisible = true;
        this.$nextTick(() => {
          this.$refs.faceCheck.openCamera();
        });
      }
    },
    // 关闭前回调
    testDialogClose() {
      if (this.$refs.timer !== undefined) {
        this.$refs.timer.deleteTimer();
      }
      _this.toTestVisible = false;
      _this.step = "";
      _this.isFull = false; //是否全屏显示
      _this.isUploaded = false; //验证上传
      _this.isBegin = false; //考试是否开始
      _this.choseExam = {};
      _this.remainTime = 0;
      _this.systemTime = 0;
    },
    // 验证上传后的执行逻辑
    async nextStep(url) {
      let log = {
        fkExamUuid: _this.choseExam.examUuid,
        fkExamName: _this.choseExam.examName,
        info: url,
        operation: "登录-人脸拍照验证",
      };
      _this.saveProctorLog(log);
      _this.isUploaded = true;
      await _this.$post("/sys/timestamp", null).then((res) => {
        if (res.code === 0) {
          _this.systemTime = res.data;
        }
      });
      let timestr = _this.formatDate(_this.choseExam.beginTime);
      let temp = new Date(timestr).getTime() - _this.systemTime;
      if (temp > 0) {
        _this.remainTime = temp;
        _this.$nextTick(() => {
          _this.$refs.timer.initTimer();
        });
      } else {
        _this.isBegin = true;
      }
      _this.screenShare();
      _this.initWebsocket();
    },
    // 考试开始计时器结束
    firstEndTimer() {
      this.$post("/exam/getone", { examUuid: _this.choseExam.examUuid }).then((res) => {
        if (res.code === 0 && res.data.status === 1) {
          _this.isBegin = true;
        } else {
          setTimeout(function () {
            this.$post("/exam/getone", { examUuid: _this.choseExam.examUuid }).then(
              (res) => {
                if (res.code === 0 && res.data.status === 1) {
                  _this.isBegin = true;
                }
              }
            );
          }, 1500);
        }
      });

      this.screenShare();

      this.$nextTick(() => {
        this.$refs.timer.deleteTimer();
      });
    },
    // 开始考试后的执行逻辑
    async openExam() {
      _this.step = "init_exam";
      this.isFull = true;
      _this.mainheight = document.documentElement.clientHeight - 95;

      //试卷初始化
      await _this.$post("/paper/getbyuuid", _this.choseExam).then((res) => {
        console.log(res);
        if (res.code === 0) {
          _this.systemTime = res.data.sysTime;
          _this.allQuestions = res.data.questionsList;
        }
      });
      let timestr = _this.formatDate(_this.choseExam.endTime);
      let temp = new Date(timestr).getTime() - _this.systemTime;
      if (temp > 0) {
        _this.remainTime = temp;
        _this.$nextTick(() => {
          _this.$refs.secondTimer.initTimer();
          _this.$refs.rightpaper.initPaper();
        });
      }
      _this.openCamera();
    },
    // 用户前置摄像头
    openCamera() {
      this.getUserMedia =
        navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia;

      this.getUserMedia.call(
        navigator,
        {
          audio: false,
          video: true,
        },
        (stream) => {
          document.getElementById("video2").srcObject = stream;
          _this.videoStream = stream;
          _this.TrackFace();
          stream.getTracks()[0].onended = (ev) => {
            if (_this.isFull && _this.isBegin && !_this.isSubmit) {
              _this.errorMediaStream("camera");
            }
          };
        },
        (error) => {
          // 处理媒体流创建失败错误
          _this.$message.error("打开摄像头失败！");
          console.log("getUserMedia error: " + error);
        }
      );
    },
    // 屏幕分享
    screenShare() {
      let displayMediaOptions = {
        audio: false,
        video: true,
      };
      let promise = navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
      promise.then((res) => {
        let track = res.getTracks()[0].label;
        if (track.indexOf("screen") === -1) {
          _this.isScreen = false;
          this.$confirm("所选不是全屏，请刷新浏览器再次进入！", "提示", {
            showClose: false,
            closeOnClickModal: false,
            closeOnPressEscape: false,
            showCancelButton: false,
            confirmButtonText: "确认",
            type: "error",
          }).catch(() => {});
          return;
        } else {
          _this.isScreen = true;
          _this.screenStream = res;
        }
        res.getTracks()[0].onended = (ev) => {
          if (!_this.isSubmit) {
            _this.errorMediaStream("screen");
          }
        };
      });
    },
    // 人脸检测
    TrackFace() {
      let tracker = new tracking.ObjectTracker("face");
      tracker.setInitialScale(1);
      tracker.setStepSize(1);
      tracker.setEdgesDensity(0.1);

      this.trackItem = tracking.track("#video2", tracker, {});

      tracker.on("track", (event) => {
        if (event.data.length > 0) {
          _this.hasFace = true;
          setTimeout(function () {
            _this.checkFace();
          }, 5000);
        } else {
          _this.hasFace = false;
        }
      });
    },
    // 用户非法关闭流
    errorMediaStream(type) {
      console.log("type", type);
      let str = "非法关闭摄像头";
      if (type === "screen") {
        str = "非法关闭屏幕共享";
      }

      let log = {
        fkExamUuid: _this.choseExam.examUuid,
        fkExamName: _this.choseExam.examName,
        info: new Date() + str,
        operation: str,
      };
      _this.saveProctorLog(log);

      if (type === "screen") {
        this.$confirm(str + "！已上传记录！！将刷新页面重新进入考试", "提示", {
          showClose: false,
          closeOnClickModal: false,
          closeOnPressEscape: false,
          showCancelButton: false,
          confirmButtonText: "确认",
          type: "error",
        })
          .then(() => {
            location.reload(true)
          })
          .catch(() => {});
      } else if (type === "camera") {
        this.$confirm(
          str + "！已上传记录！！请重新开启浏览器权限，再次进入考试！",
          "提示",
          {
            showClose: false,
            closeOnClickModal: false,
            closeOnPressEscape: false,
            showCancelButton: false,
            confirmButtonText: "确认",
            type: "error",
          }
        )
          .then(() => {
            _this.screenShare();
          })
          .catch(() => {});
      }
    },
    // 拍照
    checkFace() {
      // if (_this.hasFace) {
      //   console.log("还是没有头像");
      // }
    },
    //考试结束计时结束
    async secondEndTimer() {
      await this.submitPaper()
    },
    //手动提交试卷
    async submitPaper() {
      _this.dialogLoading = true;
      let multis = JSON.parse(JSON.stringify(_this.$refs.rightpaper.multiQuestuins));
      multis.forEach((element) => {
        if (element.answer !== null) {
          let str = JSON.stringify(element.answer);
          element.answer = str;
        }
      });
      let questlist = new Array();
      questlist = questlist.concat(_this.$refs.rightpaper.radioQuestuins);
      questlist = questlist.concat(multis);
      questlist = questlist.concat(_this.$refs.rightpaper.tureQuestuins);
      questlist = questlist.concat(_this.$refs.rightpaper.shortQuestuins);
      let param = {
        exam: _this.choseExam,
        // radioQuestuins: _this.$refs.rightpaper.radioQuestuins,
        // multiQuestuins: multis,
        // tureQuestuins: _this.$refs.rightpaper.tureQuestuins,
        // shortQuestuins: _this.$refs.rightpaper.shortQuestuins,
        allQuestions: questlist,
      };
      await _this.$post("/stuanswer/submitpaper", param).then((res) => {
        if (res.code === 0) {
          _this.isSubmit = true;
          _this.dialogLoading = false;
          _this.toTestVisible = false;
          _this.destoryPaper();
          _this.$message.success("提交成功！")
        }
      });
    },
    // 关闭考试
    destoryPaper() {
      if (_this.screenStream instanceof MediaStream && _this.screenStream.getTracks() !== undefined) {
        _this.screenStream.getTracks()[0].stop();
      }
      if (_this.videoStream instanceof MediaStream && _this.videoStream.getTracks() !== undefined) {
        _this.videoStream.getTracks()[0].stop();
      }
      _this.step = "check_face";
    },
    //搜索
    handleSearch() {
      this.getData();
    },
    // 上传监考日志
    saveProctorLog(log) {
      _this.$post("/proctorlog/insertone", log).then((res) => {
        if (res.code !== 0) {
          console.log("保存信息失败");
        }
      });
    },
    // 获取课程
    getCourse() {
      this.$post("/course/byuser", null).then((res) => {
        if (res.code === 0) {
          this.courses = res.data;
        }
      });
    },
    // 获取考试列表数据
    getData() {
      this.$post("/exam/getistested", this.queryParam).then((res) => {
        if (res.code === 0) {
          this.exams = res.data;
        }
      });
    },
    formatDate(date) {
      return DateUtils.utc2beijing(date);
    },

    /**
     * 以下为websocket 连接所需
     */
    initWebsocket() {
      if ("WebSocket" in window) {
        _this.websocket = new WebSocket(
          _this.websocketUrl + _this.loginToken + "/" + _this.choseExam.examUuid
        );
        console.log("链接已建立");
        _this.isWebscoket = true;
        // 接收服务器数据
        _this.websocket.onmessage = (evt) => {
          const receivedMsg = evt.data;
          const jsonMsg = JSON.parse(receivedMsg);
          if (jsonMsg !== null && jsonMsg !== "") {
            var head = jsonMsg.head;
            var data = jsonMsg.data;
            // 处理信息
            switch (head) {
              case "sendTo":
                _this.sendTo(data);
                break;
              case "videoCall":
                _this.videocall(jsonMsg, data.fromUser);
                break;
            }
          }
        };
      } else {
        _this.$message.error("与服务器通讯连接建立失败！");
        _this.isWebscoket = false;
      }
    },
    // 发送消息 私发
    sendMessage(message) {
      this.websocket.send(message);
    },
    // 接收私发消息
    sendTo(data) {
      var jsonData = JSON.parse(data);
      let pc;
      if (
        _this.teacherMap.get(jsonData.fromUser) === null ||
        _this.teacherMap.get(jsonData.fromUser) === undefined
      ) {
        pc = new RTCPeerConnection(_this.iceServer);
        _this.teacherMap.set(jsonData.fromUser, pc);
      }
      // 判断消息是否为视频通话请求
      if (jsonData.title === "From_Teacher_Call") {
          const str = {
            head: "sendTo",
            data: {
              fromUser: _this.studentId,
              toUser: jsonData.fromUser,
              title: "Request_Call",
            },
          };
          _this.sendMessage(JSON.stringify(str)) 
      }else if (jsonData.title === "Request_Call") {
        // 接收视频通话
        // _this.isCaller = false;
        // _this.receiveCall(false, jsonData.fromUser, pc);
        // const str = {
        //   head: "sendTo",
        //   data: {
        //     fromUser: _this.studentId,
        //     toUser: jsonData.fromUser,
        //     title: "Receive_Call",
        //   },
        // };
        // this.sendMessage(JSON.stringify(str));
      } else if (jsonData.title === "Receive_Call") {
        _this.isCaller = true;
        _this.receiveCall(true, jsonData.fromUser, pc);
      } else {
        console.log(jsonData);
      }
    },
    // 接收视频通话 进行webrtc通信操作
    receiveCall(isCaller, user, pc) {
      // 发送ICE候选到其他客户端
      _this.teacherMap.get(user).onicecandidate = (event) => {
        if (event.candidate !== null) {
          let str = JSON.stringify({
            head: "videoCall",
            data: {
              fromUser: _this.studentId,
              toUser: user,
              event: "_ice_candidate",
              data: {
                candidate: event.candidate,
              },
            },
          });
          _this.sendMessage(str);
        }
      };

      // 如果检测到媒体流连接到本地，将其绑定到一个video标签上输出
      _this.teacherMap.get(user).onaddstream = (event) => {
        console.log(event);
      };

      _this.teacherMap.get(user).addStream(_this.screenStream);
      // 如果是发起方则发送一个offer信令
      if (isCaller) {
        _this.teacherMap.get(user).createOffer(
          (desc) => {
            _this.teacherMap.get(user).setLocalDescription(desc);
            const str = JSON.stringify({
              head: "videoCall",
              data: {
                fromUser: _this.studentId,
                toUser: user,
                event: "_offer",
                data: {
                  sdp: desc,
                },
              },
            });
            _this.sendMessage(str);
          },
          function (error) {
            console.log("Failure callback: " + error);
          }
        );
      }
    },
    videocall(event, user) {
      let pc = _this.teacherMap.get(user);
      var json = event.data;
      // 如果是一个ICE的候选，则将其加入到PeerConnection中，否则设定对方的session描述为传递过来的描述
      if (json.event === "_ice_candidate") {
        pc.addIceCandidate(new RTCIceCandidate(json.data.candidate));
      } else {
        pc.setRemoteDescription(new RTCSessionDescription(json.data.sdp));
        // 如果是一个offer，那么需要回复一个answer
        if (json.event === "_offer") {
          pc.createAnswer(
            (desc) => {
              _this.teacherMap.get(user).setLocalDescription(desc);
              const str = JSON.stringify({
                head: "videoCall",
                data: {
                  fromUser: _this.studentId,
                  toUser: user,
                  event: "_answer",
                  data: {
                    sdp: desc,
                  },
                },
              });
              _this.sendMessage(str);
            },
            function (error) {
              console.log("Failure callback: " + error);
            }
          );
        }
      }
    },
  },
  created() {
    _this = this;
    this.getCourse();
    this.getData();
  },
  watch: {
    queryParam: {
      handler: (val, oldval) => {
        if (_this.queryParam.fkCourseName !== null) {
          _this.getData();
        }
      },
      deep: true,
    },
  },
  mounted() {},
};
</script>
<style scoped>
.card {
  margin: 0 5px 15px 5px;
  width: 24%;
  border-radius: 15px;
  display: inline-block;
  background-color: #f5f5f5;
  /* background-image: linear-gradient(141deg, #a7cffcec 0%, #a7cffcec 51%, #a7cffcec 75%); */
}
.span_label {
  display: inline-block;
  font-weight: bold;
  vertical-align: middle;
}
.span_info {
  display: inline-block;
  white-space: nowrap; /*强制单行显示*/
  text-overflow: ellipsis; /*超出部分省略号表示*/
  overflow: hidden; /*超出部分隐藏*/
  max-width: 60%;
  vertical-align: middle;
}
.item {
  margin: 4px;
}
.titlewin {
}
.leftwin {
  width: 300px;
}
.rightwin {
}
/deep/el-tag {
  background-color: rgba(64, 158, 255, 0.1);
  display: inline-block;
  padding: 0 10px;
  height: 32px;
  line-height: 30px;
  font-size: 15px;
  color: #409eff;
  border-radius: 4px;
  box-sizing: border-box;
  border: 1px solid rgba(64, 158, 255, 0.2);
  white-space: nowrap;
}
</style>
